package com.xzzz.irda.guardcat.core.metric.app;

import cn.hutool.core.map.MapUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * 获取本应用调用的全部 FEIGN 服务
 */
@Slf4j
@ConditionalOnClass(value = EnableFeignClients.class)
public class FeignRelationUtil implements BeanPostProcessor, ResourceLoaderAware, EnvironmentAware {

    private static final Set<String> RELATION = new HashSet<>();
    private Environment environment;
    private ResourceLoader resourceLoader;
    private static final String basePackage = "com.xzzz.backend";

    @PostConstruct
    public void init() {
        log.info("应用包含FEIGN模块，已解析FEIGN调用关系");

        //
        LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();

        // classpath 扫描器
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        // 扫描器只扫描路径下的 FeignClient 注解
        scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
        candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
        for (BeanDefinition candidateComponent : candidateComponents) {
            if (candidateComponent instanceof AnnotatedBeanDefinition) {
                // verify annotated class is an interface
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
                Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");

                Map<String, Object> attributes = annotationMetadata
                        .getAnnotationAttributes(FeignClient.class.getCanonicalName());
                assert attributes != null;
                if (MapUtil.isNotEmpty(attributes)) {
                    RELATION.add((String) attributes.get("value"));
                }
            }
        }
    }

    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    public static Set<String> getRelation() {
        return RELATION;
    }
}
